﻿using System.ComponentModel;
using System.Reflection;
using SManaModel.dtos;

namespace SManaModel.utils;

public static class ReflectionExtension
{
    public static T? GetAttribute<T>(this PropertyInfo? propertyInfo) where T : Attribute
    {
        if (propertyInfo == null)
            return null;

        var v = propertyInfo.GetCustomAttribute<T>();
        return v;
    }

    public static TOut? GiveTo<TOut>(this object @in)
    {
        var tinProperties = @in.GetType().GetProperties();
        var toutObj = Activator.CreateInstance(typeof(TOut));
        var toutProperties = typeof(TOut).GetProperties();
        foreach (var toutProperty in toutProperties)
        {
            var outDescriptionName = toutProperty.GetAttribute<DescriptionAttribute>()?.Description;
            foreach (var tinProperty in tinProperties)
            {
                var inDescriptionName = tinProperty.GetAttribute<DescriptionAttribute>()?.Description;
                // 这里为啥不写成一句表达式, 这样更加容易理解
                if (outDescriptionName != null && outDescriptionName.Equals(tinProperty.Name, StringComparison.InvariantCultureIgnoreCase))
                {
                    toutProperty.SetValue(toutObj, tinProperty.GetValue(@in));
                    break;
                }

                if (inDescriptionName != null && inDescriptionName.Equals(toutProperty.Name, StringComparison.InvariantCultureIgnoreCase))
                {
                    toutProperty.SetValue(toutObj, tinProperty.GetValue(@in));
                    break;
                }

                if (toutProperty.Name.Equals(tinProperty.Name, StringComparison.InvariantCultureIgnoreCase))
                {
                    toutProperty.SetValue(toutObj, tinProperty.GetValue(@in));
                    break;
                }
            }
        }
        TOut? targetObj = (TOut)toutObj;
        return targetObj;
    }

    public static TOut? ConvertTo<TOut>(this object @in)
    {
        var tinProperties = @in.GetType().GetProperties();
        var toutObj = Activator.CreateInstance(typeof(TOut));
        var toutProperties = typeof(TOut).GetProperties();
        foreach (var toutProperty in toutProperties)
        {
            var outDescriptionName = toutProperty.GetAttribute<DescriptionAttribute>()?.Description;
            foreach (var tinProperty in tinProperties)
            {
                var inDescriptionName = tinProperty.GetAttribute<DescriptionAttribute>()?.Description;
                var oldValue = tinProperty.GetValue(@in);
                if (oldValue is IEnumerable<object> objects
                    && !objects.Any()
                    || oldValue is null)
                {
                    continue;
                }

                // 这里为啥不写成一句表达式, 这样更加容易理解
                if (outDescriptionName != null && outDescriptionName.Equals(tinProperty.Name, StringComparison.InvariantCultureIgnoreCase))
                {
                    toutProperty.SetValue(toutObj, tinProperty.GetValue(@in));
                    break;
                }

                if (inDescriptionName != null && inDescriptionName.Equals(toutProperty.Name, StringComparison.InvariantCultureIgnoreCase))
                {
                    toutProperty.SetValue(toutObj, tinProperty.GetValue(@in));
                    break;
                }

                if (toutProperty.Name.Equals(tinProperty.Name, StringComparison.InvariantCultureIgnoreCase))
                {
                    toutProperty.SetValue(toutObj, tinProperty.GetValue(@in));
                    break;
                }
            }
        }
        var targetObj = (TOut)toutObj;
        return targetObj;
    }

    public static List<TOut> ConvertTo<TOut>(this IEnumerable<object> @in) =>
        @in.Select(obj => obj.ConvertTo<TOut>()).Where(outObj => outObj != null).ToList()!;


    public static TOut AssignTo<TOut>(this object @in, TOut @source)
    {
        var tinProperties = @in.GetType().GetProperties();
        var toutProperties = typeof(TOut).GetProperties();
        foreach (var toutProperty in toutProperties)
        {
            var outDescriptionName = toutProperty.GetAttribute<DescriptionAttribute>()?.Description;
            foreach (var tinProperty in tinProperties)
            {
                var inDescriptionName = tinProperty.GetAttribute<DescriptionAttribute>()?.Description;
                var oldValue = tinProperty.GetValue(@in);
                if (oldValue is IEnumerable<object> objects
                    && !objects.Any()
                    || oldValue is null)
                {
                    continue;
                }

                // 这里为啥不写成一句表达式, 这样更加容易理解
                if (outDescriptionName != null && outDescriptionName.Equals(tinProperty.Name, StringComparison.InvariantCultureIgnoreCase))
                {
                    toutProperty.SetValue(source, tinProperty.GetValue(@in));
                    break;
                }

                if (inDescriptionName != null && inDescriptionName.Equals(toutProperty.Name, StringComparison.InvariantCultureIgnoreCase))
                {
                    toutProperty.SetValue(source, tinProperty.GetValue(@in));
                    break;
                }

                if (toutProperty.Name.Equals(tinProperty.Name, StringComparison.InvariantCultureIgnoreCase))
                {
                    toutProperty.SetValue(source, tinProperty.GetValue(@in));
                    break;
                }
            }
        }
        return source;
    }

    /// <summary>
    /// 未测试
    /// </summary>
    /// <typeparam name="TTarget"></typeparam>
    /// <typeparam name="TSource"></typeparam>
    /// <param name="targetObj"></param>
    /// <param name="sourceObj"></param>
    /// <returns></returns>
    public static TTarget AssignTo<TTarget, TSource>(TTarget targetObj, TSource sourceObj)
    {
        foreach (var inP in sourceObj.GetType().GetProperties())
        {
            var inName = inP.Name;
            var inValue = inP.GetValue(sourceObj) ?? throw new ArgumentNullException(nameof(sourceObj));
            foreach (var outP in targetObj.GetType().GetProperties())
            {
                var outName = outP.Name;
                if (inName.Equals(outName, StringComparison.InvariantCultureIgnoreCase))
                {
                    if (inValue is IEnumerable<object> objects
                        && !objects.Any()
                        || inValue is null)
                    {
                        continue;
                    }
                    outP.SetValue(targetObj, inValue);
                    break;
                }
            }
        }
        return targetObj;
    }
}